﻿using NPOI.SS.UserModel;
using NPOI.Util.Collections;
using Org.BouncyCastle.Utilities.Collections;
using System;
using System.Collections.Generic;
using System.Data;
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;

namespace Single.Npoi
{
    public class NpoiEntity<T> where T : class, new()
    {
        private readonly List<T> list;

        public NpoiEntity(List<T> list)
        {
            this.list = list;
        }

        public static NpoiEntity<T> Create(List<T> list)
        {
            return new NpoiEntity<T>(list);
        }

        public static NpoiEntity<T> Create<T2>(Dictionary<string, Expression<Func<T2, object>>> maps, string path, int sheetIndex = 0)
        {
            var list = new List<T>();

            var dt = NpoiHelper.ExcelToDataTable(path, "sheet1", sheetIndex);

            foreach (DataRow row in dt.Rows)
            {
                T item = new T();

                foreach (var map in maps)
                {
                    if (dt.Columns.Contains(map.Key))
                    {
                        var prop = GetProperty(map.Value);

                        if (prop != null)
                        {
                            var value = row[map.Key];

                            if (value != null)
                            {
                                try
                                {
                                    prop.SetValue(item, Convert.ChangeType(value, prop.PropertyType));
                                }
                                catch { }
                            }
                        }
                    }
                }

                list.Add(item);
            }

            return new NpoiEntity<T>(list);
        }

        public List<T> ToCollections()
        {
            return list;
        }

        public DataTable ToDataTable(params string[] ignore)
        {
            var dt = new DataTable();

            var type = typeof(T);
            var properties = type.GetProperties();

            foreach (var prop in properties)
            {
                if (!ignore.Contains(prop.Name))
                {
                    dt.Columns.Add(prop.Name, Nullable.GetUnderlyingType(prop.PropertyType) ?? prop.PropertyType);
                }
            }

            foreach (T item in list)
            {
                var row = dt.NewRow();
                foreach (var prop in properties)
                {
                    if (!ignore.Contains(prop.Name))
                    {
                        row[prop.Name] = prop.GetValue(item) ?? DBNull.Value;
                    }
                }
                dt.Rows.Add(row);
            }

            return dt;
        }

        public DataTable ToDataTable<T2>(Dictionary<string, Expression<Func<T2, object>>> maps)
        {
            var dt = new DataTable();

            foreach (var map in maps)
            {
                dt.Columns.Add(map.Key, Nullable.GetUnderlyingType(map.Value.Body.Type) ?? map.Value.Body.Type);
            }

            foreach (T item in list)
            {
                var row = dt.NewRow();
                foreach (var map in maps)
                {
                    var prop = GetProperty(map.Value);
                    if (prop != null)
                    {
                        row[map.Key] = prop.GetValue(item) ?? DBNull.Value;
                    }
                }
                dt.Rows.Add(row);
            }

            return dt;
        }

        public void ToExcel(string filename, params string[] ignore)
        {
            NpoiHelper.DataTableToExcel(ToDataTable(ignore), "sheet1", filename);
        }

        public void ToExcel<T2>(string filename, Dictionary<string, Expression<Func<T2, object>>> maps)
        {
            NpoiHelper.DataTableToExcel(ToDataTable(maps), "sheet1", filename);
        }

        public void ToCsv<T2>(string filename, Dictionary<string, Expression<Func<T2, object>>> maps)
        {
            NpoiHelper.DataTableToCsv(ToDataTable(maps), filename);
        }

        private static PropertyInfo GetProperty<T2>(Expression<Func<T2, object>> expr)
        {
            if (expr.Body is UnaryExpression)
            {
                return ((MemberExpression)((UnaryExpression)expr.Body).Operand).Member as PropertyInfo;
            }
            else if (expr.Body is MemberExpression)
            {
                return ((MemberExpression)expr.Body).Member as PropertyInfo;
            }
            return null;
        }
    }
}